package com.itxh.mybatis.dao;

import com.itxh.mybatis.model.ParameterMapping;
import com.itxh.mybatis.po.User;
import org.apache.commons.dbcp.BasicDataSource;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;


/**
 * 持久层代码--使用JDBC实现
 * @author think
 *
 */
public class UserDaoImplWithJdbc2 implements UserDao {


	@Override
	public User queryUserById(Integer id) {
		String sql = "select * from user where id = ?";
		List<ParameterMapping> parameterMappings = new ArrayList<>();
		parameterMappings.add(new ParameterMapping("id"));

		User user = selectOne(sql, id, User.class,"prepared",parameterMappings);
		return user;
	}
	@Override
	public List<User> queryUserByName(String username) {
		String sql = "select * from user where username like '%"+username+"%'";

		return selectList(sql,username,User.class,"statement",null);
	}

	private <T> T selectOne(String sql, Object param, Class resultType, String statementType, List<ParameterMapping> parameterMappings){
		List<T> list = selectList(sql, param, resultType,statementType,parameterMappings);
		if (list != null || list.size() == 1){
			return list.get(0);
		}
		return (T) list;
	}

	private <T> List<T> selectList(String sql, Object param, Class resultType, String statementType, List<ParameterMapping> parameterMappings){
		Connection connection = null;
		Statement statement = null;

		ResultSet rs = null;

		List<T> results = new ArrayList<>();
		try {
			// 1、获取连接
			connection = getConnection();

			// 2、statement
			statement = getStatement(connection,sql,statementType);

			// 3、设置参数
			setParameters(statement,param,statementType,parameterMappings);

			// 4、执行statement
			rs = executeStatement(statement,sql,statementType);

			// 5、处理结果集
			results = handleResultSet(rs,resultType);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 6、释放资源
			releaseConnection(connection,statement,rs);
		}
		return results;
	}

	private void releaseConnection(Connection connection, Statement statement, ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (statement != null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (connection != null) {
			try {
				connection.close();
			} catch (SQLException e) {
			}
		}
	}

	private <T> List<T> handleResultSet(ResultSet rs, Class resultType) throws Exception{
		if (rs == null) {
			return null;
		}
		List<T> results = new ArrayList<>();
		Object result = null;
		while (rs.next()){
			result = newObject(resultType);

			// 结果集元数据
			ResultSetMetaData metaData = rs.getMetaData();
			int columnCount = metaData.getColumnCount();
			for (int i = 1; i <= columnCount; i++) {
				// 下标从1开始
				String columnName = metaData.getColumnName(i);
				// 通过反射，给对象赋值
				setPropretyValue(result,columnName,rs.getObject(i));
			}

			results.add((T) result);
		}
		return results;
	}

	private void setPropretyValue(Object result, String columnName, Object value) throws Exception{
		Class<?> aClass = result.getClass();
		Field field = aClass.getDeclaredField(columnName);
		field.setAccessible(true);
		field.set(result,value);
	}

	private Object newObject(Class resultType) throws Exception{
		Constructor constructor = resultType.getConstructor();
		return constructor.newInstance();
	}

	private ResultSet executeStatement(Statement statement, String sql, String statementType) throws Exception{
		if(statementType.equals("prepared")){
			PreparedStatement preparedStatement = (PreparedStatement) statement;
			return preparedStatement.executeQuery();
		}else if(statementType.equals("statement")){
			return statement.executeQuery(sql);
		}else if(statementType.equals("callable")){
			// TODO
		}else{
			// TODO
		}
		return null;
	}

	private void setParameters(Statement statement, Object param, String statementType, List<ParameterMapping> parameterMappings) throws Exception {
		if (statementType.equals("prepared")){
			PreparedStatement preparedStatement = (PreparedStatement) statement;

			// 判断入参类型，不同类型的参数，有不同的设置方法
			if(isSimpleType(param)){
				// TODO 可以针对不同类型，去设置参数
				// preparedStatement.setInt(1,param);
				// 可以使用类型处理器
				preparedStatement.setObject(1,param);
			}else if (isMap(param)){
				Map map = (Map) param;
				// 普通类
				if (parameterMappings != null){
					for (int i = 0; i < parameterMappings.size(); i++) {
						ParameterMapping pm = parameterMappings.get(i);

						String parameterName = pm.getParameterName();

						// 利用反射，可以通过属性名，得到对象的属性值
						Object value = map.get(parameterName);
						preparedStatement.setObject(i+1,value);

					}
				}
			}else if(isList(param)){
				// TODO
			}else{
				// 普通类
				if (parameterMappings != null){
					for (int i = 0; i < parameterMappings.size(); i++) {
						ParameterMapping pm = parameterMappings.get(i);

						String parameterName = pm.getParameterName();

						// 利用反射，可以通过属性名，得到对象的属性值
						Object value = resolveValue(param,parameterName);
						preparedStatement.setObject(i+1,value);

					}
				}
			}
		}
	}

	private Object resolveValue(Object param, String parameterName) throws Exception{
		Class<?> aClass = param.getClass();
		Field field = aClass.getDeclaredField(parameterName);
		field.setAccessible(true);
		return field.get(param);
	}

	private boolean isList(Object param) {
		return (param instanceof List);
	}

	private boolean isMap(Object param) {
		return (param instanceof Map);
	}

	private static Set<Class> simpleTypeSet = new HashSet<>();

	static{
		simpleTypeSet.add(Integer.class);
		simpleTypeSet.add(String.class);
		simpleTypeSet.add(Double.class);
		// TODO 补全简单类型
	}
	private boolean isSimpleType(Object param) {

		return (simpleTypeSet.contains(param.getClass()));
	}

	private Statement getStatement(Connection connection, String sql, String statementType)throws Exception {

		if (statementType.equals("prepared")){
			return connection.prepareStatement(sql);
		}else if (statementType.equals("statement")){
			return connection.createStatement();
		}else if(statementType.equals("callable")){
			return connection.prepareCall(sql);
		}else{
			return null;
		}

	}

	private Connection getConnection() throws Exception{
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql://124.221.171.208:3306/itxuhang?characterEncoding=utf-8");
		dataSource.setUsername("root");
		dataSource.setPassword("itxuhang");

		return dataSource.getConnection();

	}



}
